/*
 *  CTrackStereo.cpp
 *  Gangstar2
 *
 *  Created by covey on 27/01/09.
 *  Copyright 2009 __MyCompanyName__. All rights reserved.
 *
 */

#include "CTrackStereo.h"

#include "SoundManager.h"
#include <stdio.h>
//#include "../Defs.h"

//#include "../CGame.h"
//#include "../Common/LZMAFile.h"
//#include "./CMenuMusic.h"
#include <math.h>

#ifdef DEBUG_OPENAL
#define LOG_OPENAL_ERROR	setAlError(alGetError());
#else
#define LOG_OPENAL_ERROR
#endif

#define SOUND_STREAM

#ifdef OS_IPHONE
ALvoid  alBufferDataStaticProc2(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq)
{
	static  alBufferDataStaticProcPtr       proc = NULL;
	
	if (proc == NULL) {
		proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic");
	}
	
	if (proc)
		proc(bid, format, data, size, freq);
	
	return;
}
#endif

////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
CTrackStereo::CTrackStereo():
//m_pState(NULL),
m_isBufferInited(false)
//m_isTremolo(false),
//m_isVibrato(false),
//m_isBending(false),
//m_needResync(false)
{
}

////////////////////////////////////////////////////////////////////////
CTrackStereo::~CTrackStereo()
{
	Cleanup();
}

void CTrackStereo::setAlError(int error)
{
	if(0 != error) 
	{
		//printf("there is an openal error = %d", error);
	}
}


////////////////////////////////////////////////////////////////////////
int CTrackStereo::Init(int channel, int rate, int bufferSize, bool looping)
{ 
	m_rate = rate;
	m_bufferSize = bufferSize;
	m_nChannel = channel;
	
	if(m_isBufferInited)
	{
		ClearSoundFile();
	}
	
	if(!m_isBufferInited)
	{
		alGenBuffers(NUM_BUFFER, uiBuffers);
		LOG_OPENAL_ERROR;
		alGenSources(1,&uiSource);
		LOG_OPENAL_ERROR;
	}
	
	if(looping)
	{ alSourcei(uiSource, AL_LOOPING, AL_TRUE); }

	//ALint iLoop;
	unsigned long count = 0;
	
	alSourceStop(uiSource);
	LOG_OPENAL_ERROR;
	alSourcei(uiSource,AL_BUFFER,0);
	LOG_OPENAL_ERROR;
	
	if(m_nChannel == 1)
	{
		ulFormat = AL_FORMAT_MONO16;
	}
	else
	{
		ulFormat = AL_FORMAT_STEREO16;
	}
	
	m_isBufferInited = true;
	iTotalBuffersProcessed = 0;
	
	return 0;
}

////////////////////////////////////////////////////////////////////////
void CTrackStereo::Cleanup()
{
	iTotalBuffersProcessed = 0;
	//SAFE_DEL(m_pState);
	
	if(0 != uiSource) {
		
		alSourceStop(uiSource);
		LOG_OPENAL_ERROR;
		
		alSourcei(uiSource, AL_BUFFER, 0);
		LOG_OPENAL_ERROR;
		
		//static char s_tmpBuffer[1];
		//for(int i = 0; i < NUM_BUFFER; ++i)
		//{
		//	ALuint uiBuffer = uiBuffers[i];
		//	alBufferData(uiBuffer, ulFormat, s_tmpBuffer, 0, m_rate);
		//	LOG_OPENAL_ERROR;
		//}
		
		alDeleteSources(1, &uiSource);
		LOG_OPENAL_ERROR;
		
		alDeleteBuffers(NUM_BUFFER, uiBuffers);
		LOG_OPENAL_ERROR;
		
		uiSource = 0;
		
		for(int i = 0; i < NUM_BUFFER; ++i)
		{
			uiBuffers[i] = 0;
		}
		
		m_isBufferInited = false;
	}
}

void CTrackStereo::Play()
{	
	float gain = 10 ;//SoundManager::getInstance().getGlobalVolumeGain();
		
	alSourcef(uiSource, AL_GAIN, gain);
	LOG_OPENAL_ERROR;	
	alSourcePlay(uiSource);	
	LOG_OPENAL_ERROR;
	
}


void CTrackStereo::Stop(void)
{
	if(uiSource)
	{
		alSourceStop(uiSource);	
		LOG_OPENAL_ERROR;
	}
}


void CTrackStereo::ClearSoundFile(void)
{
	iTotalBuffersProcessed = 0;
	//SAFE_DEL(m_pState);
}
////////////////////////////////////////////////////////////////////////
void CTrackStereo::Update(audio::OSTick totalTick, audio::s8* buffer, int bufferNo, int bufferSize)
{	
	m_totalTick = totalTick;
	//int dt = m_totalTick - GetSoundPos();
	
#ifdef SOUND_STREAM
	
	ALint iState;
	
	uiBuffer = 0;
	
	if(bufferNo == -1)
	{
		if(GetProcessedBuffer() == 0)
		{
			int sndPos = GetSoundByteOffset();
			int approxPos = ((m_totalTick * m_rate * 2 * m_nChannel) / 1000) % m_bufferSize;
			LOG("global", "0- Channel late, trying to force sync (%d//%d)\n", sndPos, approxPos );
			SetSoundByteOffset(m_bufferSize + (m_totalTick * m_rate * 2 * m_nChannel / 1000)%m_bufferSize);
			if(GetProcessedBuffer() == 0)
			{
				LOG("global", "Sync failed\n");
				return;
			}
		}
		alSourceUnqueueBuffers(uiSource, 1, &uiBuffer);
		LOG_OPENAL_ERROR;
		++iTotalBuffersProcessed;
	}
	else
	{
		uiBuffer = uiBuffers[bufferNo];
	}
	
	if(bufferSize == -1)
		bufferSize = m_bufferSize;
	
#ifndef _OS_IPHONE
	alBufferData(uiBuffer, ulFormat, buffer, bufferSize, m_rate);
	LOG_OPENAL_ERROR;
#else	
	alBufferDataStaticProc2(uiBuffer, ulFormat, buffer, bufferSize, m_rate);
	LOG_OPENAL_ERROR;
#endif	
	alSourceQueueBuffers(uiSource, 1, &uiBuffer);
	LOG_OPENAL_ERROR;
	
	alGetSourcei(uiSource, AL_SOURCE_STATE, &iState);
	LOG_OPENAL_ERROR;
	
	if(iState != AL_PLAYING && bufferNo == -1)
	{
		alGetSourcei(uiSource, AL_BUFFERS_QUEUED, &iQueuedBuffers);
		LOG_OPENAL_ERROR;
		
		if(iQueuedBuffers)
		{
			alSourcePlay(uiSource);
			LOG_OPENAL_ERROR;
		}
	}
#endif
}

////////////////////////////////////////////////////////////////////////
bool CTrackStereo::IsFinished()
{
	return m_totalTick>=m_timeLen;
}

////////////////////////////////////////////////////////////////////////
void CTrackStereo::SetVolume( int vol )
{
	float alVol = 0.0f;
#if (HAS_SND_OPTION_GAME)
	//if(CMenuMusicPlayer::GetIsMusicEnable()) {
	alVol = (float)vol / 100;
	//}
#else
	alVol = (float)vol / 100;
#endif
	
	alSourcef(uiSource,AL_GAIN,alVol);
	LOG_OPENAL_ERROR;
}
void CTrackStereo::Pause()
{
	alSourcePause(uiSource);
	LOG_OPENAL_ERROR;
}
////////////////////////////////////////////////////////////////////////
void CTrackStereo::SetPan( int pan )
{
	//#if USE_SOUNDS
	//	m_pan = pan;
	//	SND_SetChannelPan( (1 << m_nChannel), pan );
	//#endif //#if USE_SOUNDS
}
////////////////////////////////////////////////////////////////////////
int CTrackStereo::GetSourceState(void) const
{
	ALint iState;
	alGetSourcei(uiSource, AL_SOURCE_STATE, &iState);
	//LOG_OPENAL_ERROR;
	return TranslateWaveState(iState);
}
////////////////////////////////////////////////////////////////////////
CTrackStereo::EWaveStatus	CTrackStereo::TranslateWaveState(int alState) const
{
	EWaveStatus ret;
	switch(alState)
	{
		case AL_INITIAL:
			ret = k_waveStatus_initial;
			break;
		case AL_PLAYING:
			ret = k_waveStatus_playing;
			break;
		case AL_PAUSED:
			ret = k_waveStatus_pause;
			break;
		case AL_STOPPED:
			ret = k_waveStatus_stop;
			break;
		default:
			ret = k_waveStatus_unknown;
			break;
	}
	return ret;
}

void CTrackStereo::SetVolumeGain(float in_gain)
{
	if(uiSource)
	{
		if(in_gain < 0.0f)
			in_gain = 0.0f;
		else if(in_gain > 1.0f)
			in_gain = 1.0f;
		
		alSourcef(uiSource,AL_GAIN,in_gain);
		LOG_OPENAL_ERROR;
	}
}

bool CTrackStereo::ReInitSoundInstance(void)
{
	alGenBuffers(NUM_BUFFER, uiBuffers);
	LOG_OPENAL_ERROR;
	alGenSources(1,&uiSource);
	LOG_OPENAL_ERROR;
	
	alSourceStop(uiSource);
	LOG_OPENAL_ERROR;
	
	alSourcei(uiSource,AL_BUFFER,0);
	LOG_OPENAL_ERROR;
	
	static char s_tmpBuffer[1];
	for(int i = 0; i < NUM_BUFFER; ++i)
	{
		ALuint uiBuffer = uiBuffers[i];
		alBufferData(uiBuffer, ulFormat, s_tmpBuffer, 0, m_rate);
		LOG_OPENAL_ERROR;
	}
	
	for(int i = 0; i < NUM_BUFFER; i++)
	{
		alSourceQueueBuffers(uiSource, 1, &uiBuffers[i]);
		LOG_OPENAL_ERROR;
	}
	
	alSourceStop(uiSource);
	LOG_OPENAL_ERROR;
	
	return true;
}

int CTrackStereo::GetSoundPos()
{
	int position = 0;
	position = BUFFER_TIME_SIZE*GetSoundByteOffset()/m_rate;
	return (position) + iTotalBuffersProcessed*BUFFER_TIME_SIZE;
}

//void CTrackStereo::SetSoundPos(int position)
//{
//	int timeOffset = position - iTotalBuffersProcessed*BUFFER_TIME_SIZE;
//	int byteOffset = timeOffset*m_rate/BUFFER_TIME_SIZE;
//	if(byteOffset < m_rate)
//	{
//		SetSoundByteOffset(byteOffset);
//	}
//}

int CTrackStereo::GetSoundByteOffset()
{
	int byteOffset = 0;
	alGetSourcei( uiSource, AL_BYTE_OFFSET, &byteOffset);
	LOG_OPENAL_ERROR;
	return byteOffset;
}

void CTrackStereo::setLoop(bool loop)
{
	if(loop)
	{ alSourcei(uiSource, AL_LOOPING, AL_TRUE); }
	else
	{ alSourcei(uiSource, AL_LOOPING, AL_FALSE); }

}

void CTrackStereo::setSourcePosition(const float x, const float y, const float z) const
{
	alSource3f(uiSource, AL_POSITION, x, y, z);
}

void 
CTrackStereo::setPitch(float pitchmod)
{
	alSourcef(uiSource, AL_PITCH, pitchmod);
}

void
CTrackStereo::resetPitch()
{
	alSourcef(uiSource, AL_PITCH, 1.f);
}

void CTrackStereo::SetSoundByteOffset(int byteOffset)
{
	alSourcei( uiSource, AL_BYTE_OFFSET, byteOffset);
	LOG_OPENAL_ERROR;
}

int CTrackStereo::GetSoundSampleOffset()
{
	int sampleOffset = 0;
	alGetSourcei( uiSource, AL_SAMPLE_OFFSET, &sampleOffset);
	LOG_OPENAL_ERROR;
	return sampleOffset;
}

void CTrackStereo::SetSoundSampleOffset(int sampleOffset)
{
	alSourcei( uiSource, AL_SAMPLE_OFFSET, sampleOffset);
	LOG_OPENAL_ERROR;
}

int CTrackStereo::GetProcessedBuffer()
{
	alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed);
	LOG_OPENAL_ERROR;
	return iBuffersProcessed;
}


void CTrackStereo::SetResynced()
{
	m_needResync = false;
	alSourcef(uiSource,AL_GAIN, savedGain);
	LOG_OPENAL_ERROR;
}

void CTrackStereo::Reset()
{
	alSourceStop(uiSource);
	LOG_OPENAL_ERROR;
	alSourceUnqueueBuffers(uiSource, GetProcessedBuffer(), &uiBuffer);
	LOG_OPENAL_ERROR;
	//alSourcei(uiSource,AL_BUFFER,0);
	//LOG_OPENAL_ERROR;
	//alSourcePlay(uiSource);
	//LOG_OPENAL_ERROR;
}

int CTrackStereo::GetAlState()
{
	int state;
	alGetSourcei(uiSource, AL_SOURCE_STATE, &state);
	return state;
}

int CTrackStereo::GetQueuedBuffer()
{
	int buf;
	alGetSourcei(uiSource, AL_BUFFERS_QUEUED, &buf);
	return buf;
}

